package com.alibaba.smart.framework.engine.behavior.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import com.alibaba.smart.framework.engine.SmartEngine;
import com.alibaba.smart.framework.engine.bpmn.assembly.task.UserTask;
import com.alibaba.smart.framework.engine.common.util.CollectionUtil;
import com.alibaba.smart.framework.engine.common.util.MarkDoneUtil;
import com.alibaba.smart.framework.engine.configuration.IdGenerator;
import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
import com.alibaba.smart.framework.engine.configuration.TaskAssigneeDispatcher;
import com.alibaba.smart.framework.engine.constant.TaskInstanceConstant;
import com.alibaba.smart.framework.engine.context.ExecutionContext;
import com.alibaba.smart.framework.engine.exception.EngineException;
import com.alibaba.smart.framework.engine.extension.constant.ExtensionConstant;
import com.alibaba.smart.framework.engine.instance.factory.ExecutionInstanceFactory;
import com.alibaba.smart.framework.engine.instance.factory.TaskInstanceFactory;
import com.alibaba.smart.framework.engine.instance.impl.DefaultTaskAssigneeInstance;
import com.alibaba.smart.framework.engine.instance.storage.ExecutionInstanceStorage;
import com.alibaba.smart.framework.engine.instance.storage.TaskAssigneeStorage;
import com.alibaba.smart.framework.engine.instance.storage.TaskInstanceStorage;
import com.alibaba.smart.framework.engine.model.instance.ActivityInstance;
import com.alibaba.smart.framework.engine.model.instance.ExecutionInstance;
import com.alibaba.smart.framework.engine.model.instance.InstanceStatus;
import com.alibaba.smart.framework.engine.model.instance.TaskAssigneeCandidateInstance;
import com.alibaba.smart.framework.engine.model.instance.TaskAssigneeInstance;
import com.alibaba.smart.framework.engine.model.instance.TaskInstance;
import com.alibaba.smart.framework.engine.service.param.query.TaskInstanceQueryParam;

/**
 * Created by 高海军 帝奇 74394 on  2020-03-16 00:19.
 */
public class UserTaskBehaviorHelper {

  static List<TaskAssigneeCandidateInstance> getTaskAssigneeCandidateInstances(ExecutionContext context,
                                                                               UserTask userTask) {
    TaskAssigneeDispatcher taskAssigneeDispatcher = context.getProcessEngineConfiguration()
        .getTaskAssigneeDispatcher();

    if (null == taskAssigneeDispatcher) {
      throw new EngineException("The taskAssigneeService can't be null for UserTask feature");
    }

    return taskAssigneeDispatcher.getTaskAssigneeCandidateInstance(userTask, context);
  }


  /**
   * 查找低优先级的审批人
   *
   * @param taskAssigneeCandidateInstanceList
   * @return
   */
  static List<TaskAssigneeCandidateInstance> findBatchOfHighestPriorityTaskAssigneeList(List<TaskAssigneeCandidateInstance> taskAssigneeCandidateInstanceList) {
    if (CollectionUtil.isEmpty(taskAssigneeCandidateInstanceList)) {
      return new ArrayList<TaskAssigneeCandidateInstance>(0);
    }
    int taskAssigneeCandidateInstanceSize = taskAssigneeCandidateInstanceList.size();
    //排序，升序
    if (CollectionUtil.isNotEmpty(taskAssigneeCandidateInstanceList)) {
      Collections.sort(taskAssigneeCandidateInstanceList, new Comparator<TaskAssigneeCandidateInstance>() {
        @Override
        public int compare(TaskAssigneeCandidateInstance one, TaskAssigneeCandidateInstance two) {
          return two.getPriority() - one.getPriority();
        }
      });
    }
    //优先级低的和所有相同的
    List<TaskAssigneeCandidateInstance> newTaskAssigneeCandidateInstanceList = new ArrayList<TaskAssigneeCandidateInstance>(taskAssigneeCandidateInstanceSize);
    int minPriority = 0;
    for (int i = 0; i < taskAssigneeCandidateInstanceSize; i++) {
      TaskAssigneeCandidateInstance instance = taskAssigneeCandidateInstanceList.get(i);
      if (i == 0) {
        minPriority = instance.getPriority();
        newTaskAssigneeCandidateInstanceList.add(instance);
      } else {
        if (instance.getPriority() == minPriority) {
          newTaskAssigneeCandidateInstanceList.add(instance);
        } else {
          break;
        }
      }
    }
    return newTaskAssigneeCandidateInstanceList;
  }

  static void buildTaskAssigneeInstance(TaskAssigneeCandidateInstance taskAssigneeCandidateInstance,
                                        List<TaskAssigneeInstance> taskAssigneeInstanceList,
                                        IdGenerator idGenerator, String tenantId) {
    TaskAssigneeInstance taskAssigneeInstance = new DefaultTaskAssigneeInstance();
    taskAssigneeInstance.setAssigneeId(taskAssigneeCandidateInstance.getAssigneeId());
    taskAssigneeInstance.setAssigneeType(taskAssigneeCandidateInstance.getAssigneeType());
    taskAssigneeInstance.setTenantId(tenantId);

    idGenerator.generate(taskAssigneeInstance);
    taskAssigneeInstanceList.add(taskAssigneeInstance);
  }

  public static void markDoneEIAndCancelTI(ExecutionContext context, ExecutionInstance executionInstance,
                                           List<ExecutionInstance> totalExecutionInstanceList, ExecutionInstanceStorage executionInstanceStorage, ProcessEngineConfiguration processEngineConfiguration) {
    // Complete all execution
    for (ExecutionInstance instance : totalExecutionInstanceList) {
      if (instance.isActive()) {
        MarkDoneUtil.markDoneExecutionInstance(instance, executionInstanceStorage,
            processEngineConfiguration);
      }
    }

    // Find all task
    TaskInstanceStorage taskInstanceStorage = processEngineConfiguration.getAnnotationScanner().getExtensionPoint(
        ExtensionConstant.COMMON, TaskInstanceStorage.class);

    List<TaskInstance> allTaskInstanceList = queryAllTaskInstanceList(executionInstance, processEngineConfiguration, taskInstanceStorage);

    // Cancel uncompleted task
    for (TaskInstance taskInstance : allTaskInstanceList) {

      //当前的taskInstance 已经在complete方法中更新过了
      if (taskInstance.getExecutionInstanceId().equals(executionInstance.getInstanceId())) {
        continue;
      }

      if (TaskInstanceConstant.COMPLETED.equals(taskInstance.getStatus())) {
        continue;
      }

      // 这里产生了db 读写访问,
      MarkDoneUtil.markDoneTaskInstance(taskInstance, TaskInstanceConstant.CANCELED, taskInstance.getStatus(), context.getRequest(), taskInstanceStorage, processEngineConfiguration);
    }
  }


  static void compensateExecutionAndTask(ExecutionContext context, UserTask userTask,
                                         ActivityInstance activityInstance, ExecutionInstance executionInstance,
                                         Map<String, TaskAssigneeCandidateInstance> taskAssigneeMap, ExecutionInstanceStorage executionInstanceStorage,
                                         ExecutionInstanceFactory executionInstanceFactory,
                                         TaskInstanceFactory taskInstanceFactory, ProcessEngineConfiguration processEngineConfiguration) {

    TaskInstanceStorage taskInstanceStorage = processEngineConfiguration.getAnnotationScanner().getExtensionPoint(
        ExtensionConstant.COMMON, TaskInstanceStorage.class);

    List<TaskInstance> allTaskInstanceList = queryAllTaskInstanceList(executionInstance, processEngineConfiguration, taskInstanceStorage);

    List<String> taskInstanceIdList = new ArrayList<String>();
    if (CollectionUtil.isNotEmpty(allTaskInstanceList)) {
      for (TaskInstance taskInstance : allTaskInstanceList) {
        taskInstanceIdList.add(taskInstance.getInstanceId());
      }
    }
    TaskAssigneeStorage taskAssigneeStorage = processEngineConfiguration.getAnnotationScanner().getExtensionPoint(
        ExtensionConstant.COMMON, TaskAssigneeStorage.class);
    Map<String, List<TaskAssigneeInstance>> taskAssigneeInstanceMap = taskAssigneeStorage.findAssigneeOfInstanceList(
        taskInstanceIdList, executionInstance.getTenantId(), processEngineConfiguration);

    if (taskAssigneeInstanceMap != null) {
      for (List<TaskAssigneeInstance> instanceList : taskAssigneeInstanceMap.values()) {
        if (CollectionUtil.isNotEmpty(instanceList)) {
          for (TaskAssigneeInstance instance : instanceList) {
            if (taskAssigneeMap.containsKey(instance.getAssigneeId())) {
              taskAssigneeMap.remove(instance.getAssigneeId());
            }
          }
        }
      }
    }
    if (taskAssigneeMap.size() > 0) {
      List<TaskAssigneeCandidateInstance> newTaskAssigneeList = UserTaskBehaviorHelper.findBatchOfHighestPriorityTaskAssigneeList(new ArrayList<TaskAssigneeCandidateInstance>(taskAssigneeMap.values()));
      for (TaskAssigneeCandidateInstance taskAssigneeCandidateInstance : newTaskAssigneeList) {
        ExecutionInstance newExecutionInstance = executionInstanceFactory.create(activityInstance, context);
        executionInstanceStorage.insert(newExecutionInstance, processEngineConfiguration);

        TaskInstance taskInstance = taskInstanceFactory.create(userTask, newExecutionInstance, context);
        taskInstance.setPriority(taskAssigneeCandidateInstance.getPriority());

        //reAssign
        taskInstance = taskInstanceStorage.insert(taskInstance, processEngineConfiguration);

        List<TaskAssigneeInstance> taskAssigneeInstanceList = new ArrayList<TaskAssigneeInstance>(2);
        IdGenerator idGenerator = context.getProcessEngineConfiguration().getIdGenerator();
        UserTaskBehaviorHelper.buildTaskAssigneeInstance(taskAssigneeCandidateInstance, taskAssigneeInstanceList, idGenerator, activityInstance.getTenantId());
        for (TaskAssigneeInstance taskAssigneeInstance : taskAssigneeInstanceList) {
          taskAssigneeInstance.setProcessInstanceId(taskInstance.getProcessInstanceId());
          taskAssigneeInstance.setTaskInstanceId(taskInstance.getInstanceId());
          taskAssigneeStorage.insert(taskAssigneeInstance, processEngineConfiguration);
        }
      }
    }
  }

  static List<TaskInstance> queryAllTaskInstanceList(ExecutionInstance executionInstance, ProcessEngineConfiguration processEngineConfiguration, TaskInstanceStorage taskInstanceStorage) {
    TaskInstanceQueryParam taskInstanceQueryParam = new TaskInstanceQueryParam();
    List<String> processInstanceIdList = new ArrayList<String>(2);
    processInstanceIdList.add(executionInstance.getProcessInstanceId());
    taskInstanceQueryParam.setProcessInstanceIdList(processInstanceIdList);
    taskInstanceQueryParam.setActivityInstanceId(executionInstance.getActivityInstanceId());
    List<TaskInstance> allTaskInstanceList = taskInstanceStorage.findTaskList(taskInstanceQueryParam,
        processEngineConfiguration);
    return allTaskInstanceList;
  }

  public static void abortAndSetNeedPause(ExecutionContext context, ExecutionInstance executionInstance, SmartEngine smartEngine) {
    context.getProcessInstance().setStatus(InstanceStatus.aborted);
    smartEngine.getProcessCommandService().abort(executionInstance.getProcessInstanceId(),
        InstanceStatus.aborted.name(), executionInstance.getTenantId());
    context.setNeedPause(true);
  }

}

